package wang.hex;


import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import sun.misc.Unsafe;

import java.io.FileOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static org.objectweb.asm.Opcodes.V1_1;

/**
 * @Author wujie
 * @Class CreateObjectSkipConstruction
 * @Description
 * @Date 2020/12/24 11:37
 */
public class CreateObjectSkipConstruction {
    private static Unsafe unsafe;

    static {
        Field theUnsafe = null;
        try {
            theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
            theUnsafe.setAccessible(true);
            unsafe = (Unsafe) theUnsafe.get(null);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 当一个类没有无参构造函数的时候,创建一个继承它的子类,添加上无参构造函数,并且跳过构造函数实例化
     * (Java 调用子类的无参构造函数的时候,也会查看父类有没有无参的构造函数,所以这里要跳过构造函数实)
     * (用于绕过 echancer.crate() 时候如果没有无参构造函数会报错)
     *
     * @param clazz
     * @param <T>
     * @return
     * @throws InstantiationException
     */
    public static <T> T getObject(Class<T> clazz) throws InstantiationException {
        return (T) unsafe.allocateInstance(clazz);
    }


    /**
     * @Author fox
     * @Class TracerNonparametricConstructionClassLoader
     * @Description 获取一个类的
     * @Date 2020/12/24 11:34
     */
    public static class FakeClassLoader extends ClassLoader {
        private static Map<String, byte[]> classBytesMap = new HashMap<String, byte[]>();
        private static Map<Class, Class> cacheClass = new HashMap<Class, Class>();

        public static Class getClassNoConstruct(Class clazz) {
            Class aClass1 = cacheClass.get(clazz);
            //从缓存中获取
            if (aClass1 != null) {
                return aClass1;
            }

            Constructor[] constructors = clazz.getConstructors();
            if (constructors.length == 0) {
                cacheClass.put(clazz, clazz);
                return clazz;
            }
            for (Constructor constructor : constructors) {
                Class[] parameterTypes = constructor.getParameterTypes();
                //本来就是无参的 不需要欺骗
                if (parameterTypes.length == 0) {
                    cacheClass.put(clazz, clazz);
                    return clazz;
                }
            }

            //缓存没有时再构造
            try {
                //创建一个无参的构造函数
                //Java 调用子类的无参构造函数的时候 也会查看父类有没有无参的构造函数
                //这里目的是为了骗过Enhancer的检测(检测方法有没有无参构造函数)
                //所以这里的类不能用反射创建 要用Unsafe的方法直接跳过构造函数
                //这才是重写Enhancer的原因
                String extend = clazz.getName().replace(".", "/");
                String create = extend + "XCreate";
                ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
                cw.visit(V1_1, ACC_PUBLIC, create, null, extend, null);
                MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
                mv.visitCode();
                mv.visitVarInsn(Opcodes.ALOAD, 0);
                mv.visitMethodInsn(Opcodes.INVOKESPECIAL, extend, "<init>", "()V");
                mv.visitInsn(Opcodes.RETURN);
                mv.visitMaxs(1, 1);
                mv.visitEnd();
                cw.visitEnd();
                FakeClassLoader createClassLoader = new FakeClassLoader();
                String replace = create.replace("/", ".");
                FileOutputStream fileOutputStream = new FileOutputStream("d:\\x.java");
                fileOutputStream.write(cw.toByteArray());
                createClassLoader.putClass(replace, cw.toByteArray());
                Class<?> aClass = createClassLoader.loadClass(replace);
                cacheClass.put(clazz, aClass);
                return aClass;
            } catch (Exception e) {

            }
            return null;
        }

        public FakeClassLoader() {

        }

        public FakeClassLoader(ClassLoader parent) {
            super(parent);
        }

        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            byte[] bytes = classBytesMap.get(name);
            return this.defineClass(name, bytes, 0, bytes.length);
        }

        public void putClass(String name, byte[] bytes) {
            classBytesMap.put(name, bytes);
        }
    }

}
